package mobi.smarthosts;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.UUID;

import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Debug;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Pair;

public class DeviceHelper {

    public static final String DEFAULT_DUMP_LOG_FILE_SIZE_KB = "512";

    public static final String TAG_ACTIVITY_THREAD = "ActivityThread";
    public static final String TAG_ACTIVITY_RUNTIME = "AndroidRuntime";
    public static final String TAG_PROCESS = "Process";
    public static final String TAG_DALVIKVM = "dalvikvm";

    /**
     * Return IEMI code of this device. Emulator don't have an IEMI code, will
     * return a fake value.
     *
     * ref: http://stackoverflow.com/questions/1972381/how-to-programmatically-get-the-devices-imei-esn-in-android
     */
    public static String getIMEICode(Context context) {
        String imeiCode = Settings.System.getString(
                context.getContentResolver(), Settings.Secure.ANDROID_ID);
        if (Utils.isEmptyString(imeiCode)) imeiCode = "I_AM_EMULATOR";
        return imeiCode;
    }

    public static String getAllowedLocationProviders(Context context) {
        return Settings.System.getString(
                context.getContentResolver(),
                Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
    }

    public static String generateDebugString(Context context) {
        Configuration conf = Configuration.getInstance();
        return context.getString(R.string.email_debug,
                UUID.randomUUID().toString(),
                Build.FINGERPRINT,
                conf.getPackageName(),
                conf.getVersionName(),
                getIMEICode(context),
                getAllowedLocationProviders(context),
                getDeviceResolutionString(context),
                conf.getServerUrl());
    }

    public static ArrayList<Uri> getDumpFiles(Context context) {
        Configuration conf = Configuration.getInstance();

        // clear temp folder and place dump file there
//        conf.clearTempFolder();
        File logFile = new File(conf.getTempDir(), "log.txt");
        logFile.delete();

        // dump preference file
        File prefDir = new File(context.getApplicationInfo().dataDir+"/shared_prefs");
        for (File prefFile : prefDir.listFiles())
            Utils.copyFile(prefFile, new File(conf.getTempDir(), prefFile.getName()));
        // dump log file
        dumpLog(new String[]{Configuration.TAG}, 0, new File(conf.getTempDir(), "log.txt"));

        // compress dump file to data file
        File dumpFile = new File(conf.getTempDir(), "dump.dat");
        CompressionHelper.packageFolders(
                new String[]{conf.getTempDir().getAbsolutePath()},
                dumpFile.getAbsolutePath());

        ArrayList<Uri> result = new ArrayList<Uri>();
        result.add(Uri.fromFile(dumpFile));
        return result;
    }

    /**
     * Dump logs to file.
     *
     * @param tags Tags to filter
     * @param size unit is KB
     * @param dumpFilePath
     * @return
     */
    public static File dumpLog(String[] tags, int size, File dumpFile) {
        // ref: http://developer.android.com/guide/developing/debugging/debugging-log.html
        ArrayList<String> commands = new ArrayList<String>();
        commands.add("logcat");
        // Set dump option
        commands.add("-d");
        // Set dump file path
        commands.add("-f");
        commands.add(dumpFile.getAbsolutePath());
        // Set output size
        commands.add("-r");
        commands.add(size == 0 ? DEFAULT_DUMP_LOG_FILE_SIZE_KB : Integer.toString(size));
        // Set format
        commands.add("-v");
        commands.add("time");
        // Filter specified logs
        for (String tag : tags) commands.add(tag+":V");
        // Add default logs
        commands.add("*:S"); // Mute other logs
        commands.add(TAG_ACTIVITY_THREAD+":E");
        commands.add(TAG_ACTIVITY_RUNTIME+":E");
        commands.add(TAG_DALVIKVM+":E");
        commands.add(TAG_PROCESS+":E");

        try {
            // Clear old dump file.
            if (dumpFile.exists()) dumpFile.delete();
            dumpFile.createNewFile();

            Process p = Runtime.getRuntime().exec(commands.toArray(new String[0]));
            p.waitFor();

            return dumpFile;
        } catch (InterruptedException e) {
            String msg = "Process is terminated unexpectedly.";
            Log.e(msg);
            throw new SmartHostsException(msg, e);
        } catch (IOException e) {
            String msg = "Dump log failed.";
            Log.e(msg);
            throw new SmartHostsException(msg, e);
        }
    }

    // ref: http://stackoverflow.com/questions/5832368/tablet-or-phone-android
    public static boolean isTabletDevice(Context context) {
        if (android.os.Build.VERSION.SDK_INT >= 11) { // honeycomb
            // test screen size, use reflection because isLayoutSizeAtLeast is
            // only available since 11
            android.content.res.Configuration con = context.getResources().getConfiguration();
            try {
                Method mIsLayoutSizeAtLeast = con.getClass().getMethod(
                        "isLayoutSizeAtLeast", int.class);
                boolean r = (Boolean) mIsLayoutSizeAtLeast.invoke(con,
                        0x04); //Configuration.SCREENLAYOUT_SIZE_XLARGE
                return r;
            } catch (Exception ex) {
                Log.i("OS version not support tablet.");
                return false;
            }
        }
        return false;
    }

    private static DisplayMetrics sMetrics;
    public static DisplayMetrics getDisplayMetrics(Activity activity) {
        if (sMetrics == null) {
            sMetrics = new DisplayMetrics();
            activity.getWindowManager().getDefaultDisplay().getMetrics(sMetrics);
        }
        return sMetrics;
    }

    public static Pair<Integer, Integer> getDeviceResolution(Context context) {
        float density = context.getResources().getDisplayMetrics().density;
        int rawWidth = context.getResources().getDisplayMetrics().widthPixels;
        int rawHeight = context.getResources().getDisplayMetrics().heightPixels;
        return new Pair<Integer, Integer>(Math.round(rawWidth/density), Math.round(rawHeight/density));
    }

    public static String getDeviceResolutionString(Context context) {
        Pair<Integer, Integer> res = getDeviceResolution(context);
        return res.first+"x"+res.second+"x"+context.getResources().getDisplayMetrics().density;
    }

    public static void logHeap() {
        Log.d("Native heap usage :: %s/%s", Debug.getNativeHeapAllocatedSize(), Debug.getNativeHeapSize());
        Log.d("Memory usage :: %s/%s", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().maxMemory());
    }
}
